//
// Created by 王岩松 on 2020/3/19.
//

#include "sound.h"

#include <utility>

sound::sound() {
    data.clear();
    fs = 0;
}
sound::sound(char *filename) {
    AudioFile<double> A;
    A.load(filename);
    int len = A.getNumSamplesPerChannel();
    fs = A.getSampleRate();
    data.resize(len);
    for(int i = 0; i < len; ++ i) {
        data[i] = A.samples[0][i];
    }
    A.clearAudioBuffer();
}
sound::sound(string filename) {
    AudioFile<double> A;
    A.load(std::move(filename));
    int len = A.getNumSamplesPerChannel();
    fs = A.getSampleRate();
    data.resize(len);
    for(int i = 0; i < len; ++ i) {
        data[i] = A.samples[0][i];
    }
    A.clearAudioBuffer();
}

sound::sound(double *a, int n, int _fs) {
    data.resize(n, 0);
    for(int i = 0; i < n; ++ i) {
        data[i] = a[i];
    }
    fs = _fs;
}
sound::sound(vector<double> &a, int _fs) {
    data.resize(a.size(), 0);
    for(int i = 0; i < a.size(); ++ i) {
        data[i] = a[i];
    }
    fs = _fs;
}
void sound::Free() {
    vector<double> ().swap(data);
}
int sound::getlength() {
    return data.size();
}
int sound::getfrequency() {
    return fs;
}
void sound::norm() {
    double MX = 0;
    for(int i = 0; i < data.size(); ++ i) {
        MX = max(MX, abs(data[i]));
    }
    for(int i = 0; i < data.size(); ++ i) {
        data[i] /= MX;
    }
}
double sound::getEnergy() {
    double E0 = 0;
    for(int i = 0; i < data.size(); ++ i) {
        E0 += data[i]*data[i];
    }
    return E0;
}
vector<double> sound::windowFun(int winWidth, int windowType) {
    const double PI = acos(-1.0);
    vector<double> WIN;
    switch (windowType) {
        case Function_RECTANGULAR:
            return vector<double>(winWidth, 1);
        case Function_TRIANGULAR:
            cout << "<< \n";
            WIN.resize(winWidth,0);
            for(int n = 0; n <= (winWidth-1)/2; ++ n) {
                WIN[n] = 2.0 * n / (winWidth-1);
            }
            for(int n = (winWidth-1)/2+1; n <= winWidth; ++ n) {
                WIN[n] = 2.0 - 2.0 * n / (winWidth-1);
            }
            return WIN;
        case Function_HANNING:
            for(int n = 1; n <= winWidth/2; ++ n) {
                WIN.push_back( (1 - cos(2.0*PI*n/(winWidth+1.0)))/ 2.0 );
            }
            if(winWidth & 1) WIN.push_back(1.0);
            for(int i = winWidth/2-1; i >= 0; -- i) {
                WIN.push_back(WIN[i]);
            }
            WIN.resize(winWidth,0.0);
            return WIN;
        case Function_HAMMING:
            WIN.resize(winWidth,0);
            for(int n = 0; n < winWidth; ++ n) {
                WIN[n] = (1 - 0.46) - 0.46 * cos(2.0*PI*n/(winWidth-1));
            }
            return WIN;
        default:
            cerr << "windowType error\n";
            assert(0);
            return {};
    }
    return {};
}

vector<vector<double>> sound::enframe(int wlen, int inc, int windowType) {
    vector<double> WIN = windowFun(wlen, windowType);
    int len = data.size();
    int nf =  floor((len-wlen+inc)/inc);
    vector<vector<double>> frameout(nf, vector<double>(wlen,0));
    auto it_data = data.begin();
    for(int i = 0; i < nf; ++ i) {
        frameout[i] = vector<double>(it_data,it_data+wlen);
        for(int j = 0; j < wlen; ++ j) {
            frameout[i][j] *= WIN[j];
        }
        it_data += inc;
    }
    vector<double>().swap(WIN);
    return frameout;
}

sound sound::getSeg(int l, int r) {
    vector<double> v;
    for(int i = l; i <= r; ++ i) {
        v.push_back(data[i]);
    }
    return sound(v,fs);
}
sound sound::getSeg(double tl, double tr) {
    return getSeg(Sampled_xToLowIndex(tl), Sampled_xToLowIndex(tr));
}
int sound::Sampled_xToLowIndex(double time) {
    int res = floor(time * fs);
    if(res < 0) res = 0;
    if(res >= data.size()) res = data.size()-1;
    return res;
}
double sound::Sampled_indexToX(int index) {
    return 1.0 * index / fs;
}

sound sound::resample(int samplingFrequency) {
    vector<double> ans;
    rresample(samplingFrequency,fs,data,ans);
    return sound(ans,samplingFrequency);
}
